package com.caishi.lkx.user.api;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.net.Ipv4Util;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.caishi.lkx.common.AdminBaseApi;
import com.caishi.lkx.common.role.Roles;
import com.caishi.lkx.common.util.IpAuthUtil;
import com.caishi.lkx.user.model.GroupModel;
import com.caishi.lkx.user.model.UserModel;
import com.caishi.lkx.user.model.re.SysGroupIpReModel;
import com.caishi.lkx.user.service.IGroupIpReService;
import com.caishi.lkx.user.service.IGroupService;
import com.caishi.lkx.user.service.IUserService;
import com.googlecode.ipv6.IPv6Address;
import com.zzw.common.Wrappers;
import com.zzw.common.entity.impl.BaseIntEntity;
import com.zzw.common.exception.BizRuntimeException;

import com.zzw.common.utils.IPUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Tag(name = "机构ip段绑定")
@RestController
@RequestMapping("/user/sys/group/ip")
public class GroupIpReApi extends AdminBaseApi<IGroupIpReService, SysGroupIpReModel, Long> {
    private static final Logger log = LoggerFactory.getLogger(GroupIpReApi.class);
    @Resource
    private IGroupIpReService groupIpReService;
    @Resource
    private IGroupService groupService;
    @Resource
    private IUserService<UserModel> userService;

    @Override
    public IGroupIpReService loadService() {
        return groupIpReService;
    }


    @GetMapping("/check")
    public void checkIpExist(@RequestParam String ipStr) {
        if (checkHave(ipToVal(ipStr), null)) {
            throw new BizRuntimeException("ip已存在");
        }
    }

    @GetMapping("/batch-check")
    public List<String> batchCheckIpExist(@RequestParam String ipStr) {
        var s = ipStr.replaceAll("\r\n|\t|\r|\n", ";");
        var split = s.split(";");
        return Stream.of(split).parallel().map(v -> {
            try {
                if (checkHave(ipToVal(v), null)) {
                    return v;
                }
                return null;
            } catch (Exception e) {
                return v;
            }
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    @Override
    protected LambdaQueryWrapper<SysGroupIpReModel> baseWrapper(SysGroupIpReModel model, JSONObject otherParam) {
        List<Long> groupIds = null;
        String groupName = otherParam.getStr("groupName");
        String startIp = otherParam.getStr("startIp");
        String endIp = otherParam.getStr("endIp");
        String endTime = otherParam.getStr("endTime");
        String expireTime = otherParam.getStr("expireTime");
        LocalDate bindDay = otherParam.get("bindDay", LocalDate.class);
        if (StrUtil.isNotBlank(groupName) || StrUtil.isNotBlank(expireTime)) {
            groupIds = groupService.selectList(Wrappers.<GroupModel>lambdaQuery().select(GroupModel::getId)
                            .like(StrUtil.isNotBlank(groupName), GroupModel::getName, groupName)
                                               )
                    .stream().map(BaseIntEntity::getId).collect(Collectors.toList());
            if (groupIds.isEmpty()) {
                groupIds.add(-1L);
            }
        }
        String s = null;
        String e = null;
        try {
            if (StrUtil.isNotBlank(startIp)) {
                var val = IPUtil.isIPv4(startIp) ? BigInteger.valueOf(Ipv4Util.ipv4ToLong(startIp)) : IPv6Address.fromString(startIp).toBigInteger();
                s = HexUtil.encodeHexStr(SysGroupIpReModel.bigInter2Bytes(val));
            }
            if (StrUtil.isNotBlank(endIp)) {
                var val = IPUtil.isIPv4(endIp) ? BigInteger.valueOf(Ipv4Util.ipv4ToLong(endIp)) : IPv6Address.fromString(endIp).toBigInteger();
                e = HexUtil.encodeHexStr(SysGroupIpReModel.bigInter2Bytes(val));
            }
        } catch (IllegalArgumentException illegalArgumentException) {
            throw new BizRuntimeException("ip格式错误");
        }

        String finalE = e;
        String finalS = s;
        return Wrappers.lambdaQuery(model.clearEmptyStringField())
                .in(CollUtil.isNotEmpty(groupIds), SysGroupIpReModel::getGroupId, groupIds)
                .lt(StrUtil.isNotEmpty(endTime), SysGroupIpReModel::getEndTime, endTime)
                .ge(ObjectUtil.isNotNull(bindDay), SysGroupIpReModel::getBindTime, null != bindDay ? bindDay.atTime(0, 0, 0) : null)
                .le(ObjectUtil.isNotNull(bindDay), SysGroupIpReModel::getBindTime, null != bindDay ? bindDay.atTime(23, 59, 59) : null)
                .nested(StrUtil.isNotEmpty(finalS) && StrUtil.isNotEmpty(finalE), n -> n.apply(String.format(" BINARY 0x%s >= min", finalS))
                        .apply(String.format(" BINARY 0x%s <= max", finalS))
                        .or(r -> r.apply(StrUtil.isNotEmpty(finalE), String.format(" BINARY 0x%s >= min", finalE))
                                .apply(StrUtil.isNotEmpty(finalE), String.format(" BINARY 0x%s <= max", finalE)))
                )
                .or(StrUtil.isNotEmpty(finalS) || StrUtil.isNotEmpty(finalE))
                .nested(StrUtil.isNotEmpty(finalS) || StrUtil.isNotEmpty(finalE), n -> n.apply(StrUtil.isNotEmpty(finalS), String.format(" binary 0x%s >= min", finalS))
                        .apply(StrUtil.isNotEmpty(finalE), String.format(" binary 0x%s <= max", finalE)))
                .or(StrUtil.isNotEmpty(finalS) && StrUtil.isNotEmpty(finalE), r -> r.apply(StrUtil.isNotEmpty(finalS), String.format(" binary 0x%s <= min", finalS))
                        .apply(StrUtil.isNotEmpty(finalE), String.format(" binary 0x%s >= max", finalE)))
                .orderByDesc(StrUtil.isNotEmpty(s), SysGroupIpReModel::getMin)
                .orderByAsc(StrUtil.isNotEmpty(e), SysGroupIpReModel::getMax)
                ;
    }

    @Override
    protected List<SysGroupIpReModel> listCover(List<SysGroupIpReModel> result, JSONObject otherParam) {
        return result.parallelStream().peek(r -> {
            if (null != r.getGroupId()) {
                GroupModel group = groupService.getById(r.getGroupId());
                if (null != group) {

                    r.setGroupName(group.getName());

                }
            }
            if (StrUtil.isNotBlank(r.getBindUserId())) {
                r.setBindUserName(userService.queryName(r.getBindUserId()));
            }
        }).collect(Collectors.toList());
    }

    @Override
    @PostMapping("/update")
    @Operation(summary = "添加")
    public SysGroupIpReModel add(@RequestBody SysGroupIpReModel entity) {
        var res = ipToVal(entity.getIp());
        boolean checkHave;
        try {
            checkHave = checkHave(res, null);
        } catch (Exception e) {
            log.error("ip格式错误",e);
            throw new BizRuntimeException("ip格式错误");
        }
        if (checkHave) {
            throw new BizRuntimeException("ip已存在");
        }
        entity.setBindTime(LocalDateTime.now());
        entity.setBindUserId(userContext.currentUserId());
        BeanUtil.copyProperties(res, entity, CopyOptions.create().setIgnoreNullValue(true));
        return groupIpReService.insert(entity);
    }

    @PostMapping("/batchAdd")
    @Operation(summary = "添加")
    public Boolean batchAdd(@RequestParam Long groupId, @RequestParam List<String> ipList, String remark) {
        String userId = userContext.currentUserId();
        LocalDateTime now = LocalDateTime.now();
        return groupIpReService.insertBatch(ipList.parallelStream().map(p -> {
            SysGroupIpReModel entity = new SysGroupIpReModel();
            entity.setIp(p);
            entity.setGroupId(groupId);
            entity.setRemark(remark);
            entity.setBindUserId(userId);
            var res = ipToVal(entity.getIp());
            if (checkHave(res, null)) {
                throw new BizRuntimeException("ip已存在");
            }
            entity.setBindTime(now);
            BeanUtil.copyProperties(res, entity, CopyOptions.create().setIgnoreNullValue(true));
            return entity;
        }).collect(Collectors.toList()), 100);
    }

    @Override
    @PutMapping("/update")
    public SysGroupIpReModel update(SysGroupIpReModel entity) {
        SysGroupIpReModel reModel = groupIpReService.getById(entity.getId());
        if (reModel.getBindTime() == null) {
            entity.setBindTime(LocalDateTime.now());
            entity.setBindUserId(userContext.currentUserId());
        }
        var res = ipToVal(entity.getIp());
        boolean checkHave;
        try {
            checkHave = checkHave(res, entity.getId());
        } catch (Exception e) {
            throw new BizRuntimeException("ip格式错误");
        }
        if (checkHave) {
            throw new BizRuntimeException("ip已存在");
        }
        BeanUtil.copyProperties(res, entity, CopyOptions.create().setIgnoreNullValue(true));
        return groupIpReService.simpleUpdate(entity);
    }

    @PutMapping("/unRelation")
    @Operation(summary = "取消关联")
    public Boolean unRelation(Long id) {
        return groupIpReService.unRelation(id);

    }

    @PutMapping("/unBind")
    @Operation(summary = "取消绑定")
    public Boolean unBind(@RequestParam Long id) {
        return groupIpReService.unBind(id);
    }

    @GetMapping("/test")
    @Roles
    public Object test(String ip) {
        return groupIpReService.getGroupIdByIp(StrUtil.isNotBlank(ip) ? ip : userContext.currentIp());
    }

    private boolean checkHave(SysGroupIpReModel r, Long ownerId) {
        var s = groupIpReService.getByBigInteger(new BigInteger(r.getVal()));
        if (s != null && !s.getId().equals(ownerId)) return true;
        s = groupIpReService.getByBigInteger(new BigInteger(r.getMin()));
        if (s != null && !s.getId().equals(ownerId)) return true;
        s = groupIpReService.getByBigInteger(new BigInteger(r.getMax()));
        if (s != null && !s.getId().equals(ownerId)) return true;
        List<SysGroupIpReModel> sysGroupIpReModels = groupIpReService.selectList(Wrappers.<SysGroupIpReModel>lambdaQuery()
                .nested(n -> n.apply(String.format(" BINARY 0x%s >= min", HexUtil.encodeHexStr(r.getMin()))))
                .apply(String.format(" BINARY 0x%s <= max", HexUtil.encodeHexStr(r.getMin())))
                .or()
                .apply(String.format(" BINARY 0x%s >= min", HexUtil.encodeHexStr(r.getMax())))
                .apply(String.format(" BINARY 0x%s <= max", HexUtil.encodeHexStr(r.getMax()))

                )
                .or()
                .nested(n -> n.apply(String.format(" binary 0x%s >= min", HexUtil.encodeHexStr(r.getMin())))
                        .apply(String.format(" binary 0x%s <= max", HexUtil.encodeHexStr(r.getMax()))))
                .or(o -> o.apply(String.format(" binary 0x%s <= min", HexUtil.encodeHexStr(r.getMin()))))
                .apply(String.format(" binary 0x%s >= max", HexUtil.encodeHexStr(r.getMax())))
        );
        if (sysGroupIpReModels.size() == 1) {
            s = sysGroupIpReModels.get(0);
        }
        if (sysGroupIpReModels.size() > 1) {
            return true;
        }
        return s != null && !s.getId().equals(ownerId);
    }

    private SysGroupIpReModel ipToVal(String ipStr) {
        var res = IpAuthUtil.ipToVal(ipStr);
        var result = new SysGroupIpReModel();
        result.setMin(res.getMin());
        result.setMax(res.getMax());
        result.setVal(res.getVal());
        result.setOmask(res.getOmask());
        result.setType(res.getType());
        return result;
    }

}
